home *** CD-ROM | disk | FTP | other *** search
Text File | 1992-09-02 | 23.0 KB | 511 lines | [TEXT/CCL2] |
- ;;;-*- Mode: Lisp; Package: (WOOD) -*-
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;
- ;; persistent-clos.lisp
- ;; Support for saving/restoring CLOS instance to/from Wood persistent heaps.
- ;;
- ;; Copyright © 1992 Apple Computer, Inc. All rights reserved.
- ;; Permission is given to use, copy, and modify this software provided
- ;; that this copyright notice is attached to all derivative works.
- ;; This software is provided "as is". Apple makes no warranty or
- ;; representation, either express or implied, with respect to this software,
- ;; its quality, accuracy, merchantability, or fitness for a particular
- ;; purpose.
- ;;
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;
- ;; Modification History
- ;;
- ;; -------------- 0.5
- ;; 06/23/92 bill New file
- ;;
-
- (in-package :wood)
-
- (defun dc-class-hash (disk-cache &optional create?)
- (let ((res (dc-%svref disk-cache $root-vector $pheap.class-hash)))
- (if (eql res $pheap-nil)
- (if create?
- (setf (dc-%svref disk-cache $root-vector $pheap.class-hash)
- (dc-make-hash-table disk-cache)))
- res)))
-
- (defun p-find-class (pheap name &optional (errorp t))
- (multiple-value-bind (pointer imm?) (%p-store-hash-key pheap name)
- (when pointer
- (let ((res (dc-find-class (pheap-disk-cache pheap) pointer imm? errorp)))
- (when res (pptr pheap res))))))
-
- (defun dc-find-class (disk-cache pointer immediate? &optional (errorp t))
- (let ((hash (dc-class-hash disk-cache)))
- (or (and hash
- (dc-gethash disk-cache pointer immediate? hash))
- (when errorp
- (error "Class named ~s not found."
- (dc-pointer-load disk-cache pointer immediate?))))))
-
- ; Will overwrite an existing class
- (defun p-make-class (pheap name slots)
- (unless (and (vectorp slots) (every 'symbolp slots))
- (error "~s is not a vector of slot names"))
- (multiple-value-bind (pointer imm?) (%p-store pheap name)
- (pptr pheap
- (dc-make-class (pheap-disk-cache pheap)
- pointer
- (%p-store pheap slots)
- imm?
- slots
- pheap))))
-
- (defun dc-make-class (disk-cache name slots &optional name-imm? slots-object pheap)
- (let* ((class (dc-make-uvector disk-cache $class-size $v_class))
- (hash (dc-class-hash disk-cache t))
- (wrapper (dc-make-class-wrapper disk-cache class slots slots-object pheap)))
- (dc-%svfill disk-cache class
- ($class.name name-imm?) name
- $class.own-wrapper wrapper)
- (dc-puthash disk-cache name name-imm? hash class)))
-
- (defun dc-make-class-wrapper (disk-cache class slots &optional slots-object pheap)
- (let ((wrapper (dc-make-vector disk-cache $wrapper-size)))
- (dc-%svfill disk-cache wrapper
- $wrapper.class class
- $wrapper.slots slots)
- (when slots-object
- (setf (gethash slots-object
- (wrapper-hash (or pheap (disk-cache-pheap disk-cache))))
- wrapper))
- wrapper))
-
- ; Access a (disk) class'es wrapper. Update it to agree with the
- ; class in memory, if there is one.
- ; Returns 2 value:
- ; 1) the (possibly new) wrapper
- ; 2) the in-memory class, or NIL if there isn't one.
- ; 3) the vector of slot names for the in-memory class, or NIL
- ; 4) true if the class'es was obsolete.
- (defun dc-update-class-wrapper (disk-cache class &optional pheap memory-class dont-update)
- (unless pheap (setq pheap (disk-cache-pheap disk-cache)))
- (if (eq memory-class :none)
- (setq memory-class nil)
- (let* ((name (pointer-load pheap (dc-%svref disk-cache class $class.name) :default disk-cache)))
- (setq memory-class (find-class name nil))))
- (let ((wrapper (dc-%svref disk-cache class $class.own-wrapper))
- (obsolete? nil)
- slot-names)
- (when memory-class
- (let ((wrapper-hash (wrapper-hash pheap)))
- (setq slot-names (wood-slot-names-vector (class-prototype memory-class)))
- (unless (eql wrapper (gethash slot-names wrapper-hash))
- (let ((old-slot-names (pointer-load pheap (dc-%svref disk-cache wrapper $wrapper.slots)
- :default disk-cache)))
- (if (equalp old-slot-names slot-names)
- (setf (gethash slot-names wrapper-hash) wrapper)
- (progn
- (setq obsolete? t)
- (unless dont-update
- (setf wrapper (dc-make-class-wrapper
- disk-cache class
- (%p-store pheap slot-names) slot-names pheap)
- (dc-%svref disk-cache class $class.own-wrapper) wrapper))))))))
- (values wrapper memory-class slot-names obsolete?)))
-
-
- ; This knows internals of MCL's CLOS implementation
- (defun class-slots-vector (class)
- (ccl::%wrapper-instance-slots
- (or (ccl::%class-own-wrapper class)
- (ccl::initialize-class-and-wrapper class))))
-
- (defun dc-make-class-slots-vector (disk-cache class &optional
- (pheap (disk-cache-pheap disk-cache)))
- (%p-store pheap (wood-slot-names-vector (class-prototype class))))
-
- (def-predicate ccl::classp (p disk-cache pointer)
- (dc-vector-subtype-p disk-cache pointer $v_class))
-
- (def-accessor class-name (p) (disk-cache pointer)
- (require-satisfies dc-classp disk-cache pointer)
- (dc-%svref disk-cache pointer $class.name))
-
- (defun (setf dc-class-name) (value disk-cache class &optional value-imm?)
- (require-satisfies dc-classp disk-cache class)
- (setf (dc-%svref disk-cache class $class.name value-imm?) value)
- (values value value-imm?))
-
- (def-accessor class-own-wrapper (p) (disk-cache pointer)
- (require-satisfies dc-classp disk-cache pointer)
- (dc-%svref disk-cache pointer $class.own-wrapper))
-
- (defun (setf dc-class-own-wrapper) (value disk-cache class &optional value-imm?)
- (require-satisfies dc-classp disk-cache class)
- (setf (dc-%svref disk-cache class $class.own-wrapper value-imm?) value)
- (values value value-imm?))
-
- (defmacro dc-wrapper-class (disk-cache wrapper)
- `(dc-uvref ,disk-cache ,wrapper $wrapper.class))
-
- (defmacro dc-wrapper-slots (disk-cache wrapper)
- `(dc-uvref ,disk-cache ,wrapper $wrapper.slots))
-
- (defmethod %p-store-object (pheap (object standard-class) descend)
- (let* ((disk-cache (pheap-disk-cache pheap))
- (descend (eq descend t))
- name imm?
- (address (maybe-cached-address pheap object
- (multiple-value-setq (name imm?)
- (%p-store pheap (class-name object)))
- (or (dc-find-class disk-cache name imm? nil)
- (progn
- (setq descend nil)
- (dc-make-class disk-cache
- name
- (dc-make-class-slots-vector
- disk-cache object pheap)
- imm?))))))
- (when descend
- (unless name
- (multiple-value-setq (name imm?) (%p-store pheap (class-name object))))
- (setf (dc-class-name disk-cache address imm?) name)
- (setf (dc-wrapper-slots disk-cache (dc-class-own-wrapper disk-cache address))
- (dc-make-class-slots-vector disk-cache object pheap)))
- address))
-
- (defun p-load-class (pheap disk-cache pointer depth subtype)
- (declare (ignore depth subtype))
- (maybe-cached-value pheap pointer
- (multiple-value-bind (name-pointer imm?) (dc-class-name disk-cache pointer)
- (let ((name (dc-pointer-load disk-cache name-pointer imm? pheap)))
- (or (find-class name nil)
- (let ((slots (pointer-load pheap
- (dc-wrapper-slots
- disk-cache
- (dc-class-own-wrapper disk-cache pointer))
- :default
- disk-cache)))
- (eval `(defclass ,name () ,(coerce slots 'list)))))))))
-
- (defmethod p-allocate-instance (pheap (class symbol))
- (p-allocate-instance pheap (or (p-find-class pheap class nil)
- (p-store pheap (find-class class)))))
-
- (defmethod p-allocate-instance (pheap (class standard-class))
- (p-%allocate-instance pheap (p-store pheap class) class))
-
- (defmethod p-allocate-instance (pheap (class pptr))
- (require-satisfies p-classp class)
- (p-%allocate-instance pheap class nil))
-
- (defun p-%allocate-instance (pheap class memory-class)
- (pptr pheap (dc-%allocate-instance (pheap-disk-cache pheap) (pptr-pointer class) memory-class)))
-
- (defun dc-%allocate-instance (disk-cache class &optional memory-class)
- (let* ((wrapper (dc-update-class-wrapper disk-cache class nil memory-class))
- (slots (dc-make-vector
- disk-cache
- (dc-length disk-cache (dc-wrapper-slots disk-cache wrapper))
- nil (%unbound-marker) t))
- (res (dc-make-uvector disk-cache $instance-size $v_instance)))
- (dc-%svfill disk-cache res
- $instance.wrapper wrapper
- $instance.slots slots)
- res))
-
- (def-predicate ccl::standard-instance-p (p disk-cache pointer)
- (dc-vector-subtype-p disk-cache pointer $v_instance))
-
- (def-accessor ccl::instance-class-wrapper (p) (disk-cache pointer)
- (require-satisfies dc-standard-instance-p disk-cache pointer)
- (dc-%svref disk-cache pointer $instance.wrapper))
-
- ; This is the wrong name. Check the MOP
- (def-accessor instance-access (p index) (disk-cache pointer)
- (require-satisfies dc-standard-instance-p disk-cache pointer)
- (dc-uvref disk-cache (dc-%svref disk-cache pointer $instance.slots) index))
-
- (defun (setf p-instance-access) (value p index)
- (setq index (require-type index 'fixnum))
- (if (pptr-p p)
- (let ((pheap (pptr-pheap p)))
- (multiple-value-bind (v imm?) (%p-store pheap value)
- (setf (dc-instance-access
- (pheap-disk-cache pheap) (pptr-pointer p) index imm?)
- v)
- (if imm? v (pptr pheap v))))
- (error "~s is defined only for Wood instances" '(setf p-instance-access))))
-
- (defun (setf dc-instance-access) (value disk-cache pointer index value-imm?)
- (require-satisfies dc-standard-instance-p disk-cache pointer)
- (setf (dc-uvref disk-cache (dc-%svref disk-cache pointer $instance.slots)
- index value-imm?)
- value))
-
- (defun instance-access (thing index)
- (declare (ignore thing index))
- (error "~s is defined only for Wood instances" 'instance-access))
-
- ; Instance is an on-disk address.
- ; class is an in-memory class or NIL.
- ; Returns three values:
- ; 1) The slots vector on disk
- ; 2) The slot names vector in memory.
- ; 3) slot-names vector if the instance was obsolete.
- ; This will be different from the second value if the
- ; dont-update arg is true.
- ;
- ; This is hairy because it has to deal with a lot of possibilities:
- ;
- ; 1) Class exists in memory, but hasn't been associated with PHEAP yet.
- ; 2) Class exists in memeory and has been associated with PHEAP.
- ; 3) Class does not exist in memory.
- ; 4) 1 or 2 and the class has been redefined since the instance was stored in the PHEAP.
- (defun dc-updated-instance-slots (disk-cache instance memory-class pheap &optional
- dont-update)
- (let ((old-wrapper (dc-%svref disk-cache instance $instance.wrapper))
- (instance-slots (dc-%svref disk-cache instance $instance.slots))
- class wrapper slot-names old-slot-names obsolete?)
- (if memory-class
- (progn
- (setq slot-names (wood-slot-names-vector (class-prototype memory-class)))
- (setq wrapper (gethash slot-names (wrapper-hash pheap))))
- (progn
- (setq class (dc-%svref disk-cache old-wrapper $wrapper.class))
- (multiple-value-setq (wrapper memory-class slot-names obsolete?)
- (dc-update-class-wrapper disk-cache class pheap nil dont-update))
- (unless slot-names
- (setq slot-names (pointer-load pheap (dc-%svref disk-cache old-wrapper $wrapper.slots)
- :default disk-cache)
- wrapper old-wrapper))))
- (if (if (and wrapper (not obsolete?))
- (eql wrapper old-wrapper)
- (when (equalp slot-names
- (setq old-slot-names
- (pointer-load pheap (dc-%svref disk-cache old-wrapper $wrapper.slots)
- :default disk-cache)))
- (setq wrapper (setf (gethash slot-names (wrapper-hash pheap)) old-wrapper))))
- ; Wrapper is current
- (values instance-slots slot-names)
- ; Wrapper needs updating.
- (if dont-update
- (values instance-slots old-slot-names slot-names)
- (let* ((slot-count (length slot-names))
- (slot-values (make-array slot-count))
- (slot-imms (make-array slot-count)))
- (declare (fixnum slot-count)
- (dynamic-extent slot-values slot-imms))
- (unless old-slot-names
- (setq old-wrapper (dc-%svref disk-cache instance $instance.wrapper)
- old-slot-names (pointer-load
- pheap
- (dc-%svref disk-cache old-wrapper $wrapper.slots)
- :default disk-cache)))
- (unless wrapper
- (let ((class (dc-%svref disk-cache old-wrapper $wrapper.class)))
- (setq wrapper (dc-update-class-wrapper disk-cache class pheap memory-class dont-update))))
- (dotimes (i slot-count)
- (let ((index (position (svref slot-names i) old-slot-names :test 'eq)))
- (if index
- (multiple-value-bind (value imm?) (dc-uvref disk-cache instance-slots index)
- (setf (svref slot-values i) value
- (svref slot-imms i) imm?))
- (setf (svref slot-values i) (%unbound-marker)
- (svref slot-imms i) t))))
- (let* ((old-instance-length (dc-length disk-cache instance-slots))
- (new-instance-slots (if (>= old-instance-length slot-count)
- (let ((index slot-count))
- (dotimes (i (- old-instance-length slot-count))
- (setf (dc-uvref disk-cache instance-slots index t)
- (%unbound-marker)))
- instance-slots)
- (dc-make-vector
- disk-cache slot-count
- (dc-area disk-cache instance-slots)
- (%unbound-marker) t))))
- (dotimes (i slot-count)
- (let ((value (svref slot-values i))
- (imm? (svref slot-imms i)))
- (unless (and imm? (eq value (%unbound-marker)))
- (setf (dc-%svref disk-cache new-instance-slots i imm?) value))))
- (setf (dc-%svref disk-cache instance $instance.wrapper) wrapper
- (dc-%svref disk-cache instance $instance.slots) new-instance-slots)
- (values new-instance-slots slot-names t)))))))
-
- (def-predicate ccl::standard-instance-p (p disk-cache pointer)
- (and (dc-uvectorp disk-cache pointer)
- (eq (dc-%vector-subtype disk-cache pointer) $v_instance)))
-
- (def-accessor slot-value (p slot-name) (disk-cache pointer)
- (require-satisfies dc-standard-instance-p disk-cache pointer)
- (multiple-value-bind (value imm?)
- (dc-%slot-value disk-cache pointer slot-name)
- (if (and imm? (eq value (%unbound-marker)))
- (dc-slot-unbound disk-cache pointer slot-name)
- (values value imm?))))
-
- (defun dc-%slot-value (disk-cache pointer slot-name)
- (multiple-value-bind (slots index)
- (dc-%slot-vector-and-index disk-cache pointer slot-name t)
- (if slots
- (if (eq slots (%unbound-marker))
- (values slots t)
- (dc-%svref disk-cache slots index))
- (dc-slot-missing disk-cache pointer slot-name 'slot-value))))
-
- (defun dc-slot-missing (disk-cache pointer slot-name operation &optional new-value)
- (declare (ignore operation new-value))
- (error "~s has no slot named ~s"
- (pptr (disk-cache-pheap disk-cache) pointer) slot-name))
-
- (defun dc-slot-unbound (disk-cache pointer slot-name)
- (error "Slot ~s is unbound in ~s"
- slot-name (pptr (disk-cache-pheap disk-cache) pointer)))
-
- ; Returns two values:
- ; 1) disk-cache vector of slots
- ; 2) index in the vector
- ;
- ; If the slot doesn't exist, returns NIL.
- ; If the slot exists, but only after the instance is updated and dont-update
- ; is true, returns (%unbound-marker).
- (defun dc-%slot-vector-and-index (disk-cache pointer slot-name &optional dont-update)
- (let* ((pheap (disk-cache-pheap disk-cache))
- (wrapper (dc-%svref disk-cache pointer $instance.wrapper))
- (memory-class (pointer-load
- pheap
- (dc-%svref disk-cache
- (dc-%svref disk-cache wrapper $wrapper.class)
- $class.name)
- :default disk-cache)))
- (multiple-value-bind (slots slot-names real-slot-names)
- (dc-updated-instance-slots
- disk-cache pointer
- (find-class
- memory-class
- nil)
- pheap
- dont-update)
- (let ((index (position slot-name slot-names :test 'eq))
- (real-index (and dont-update
- real-slot-names
- (position slot-name real-slot-names))))
- (if (and index (or (not dont-update) (not real-slot-names) real-index))
- (values slots index)
- (if real-index
- (%unbound-marker)
- nil))))))
-
- (defun (setf p-slot-value) (value p slot-name)
- (if (pptr-p p)
- (let* ((pheap (pptr-pheap p))
- (disk-cache (pheap-disk-cache pheap))
- (pointer (pptr-pointer p)))
- (multiple-value-bind (slots index)
- (dc-%slot-vector-and-index disk-cache pointer slot-name)
- (unless slots
- (dc-slot-missing disk-cache pointer slot-name '(setf p-slot-value)))
- (multiple-value-bind (v imm?) (%p-store pheap value)
- (setf (dc-%svref disk-cache slots index imm?) v)
- (if imm?
- v
- (pptr pheap v)))))))
-
- (def-accessor slot-boundp (p slot-name) (disk-cache pointer)
- (values (not (eq (dc-%slot-value disk-cache pointer slot-name)
- (%unbound-marker)))
- t))
-
- (def-accessor slot-makunbound (p slot-name) (disk-cache pointer)
- (multiple-value-bind (slots index)
- (dc-%slot-vector-and-index disk-cache pointer slot-name t)
- (unless slots
- (dc-slot-missing disk-cache pointer slot-name 'p-slot-makunbound))
- (unless (eq slots (%unbound-marker))
- (setf (dc-%svref disk-cache slots index t) (%unbound-marker)))
- pointer))
-
- (defmethod %p-store-object (pheap (object ccl::funcallable-standard-object) descend)
- (declare (ignore pheap descend))
- (error "Can't save generic functions yet. Maybe never."))
-
- ; this will do the wrong thing if anyone redefines the class
- ; of the object while it is running.
- (defmethod %p-store-object (pheap (object standard-object) descend)
- (let* ((class (class-of object)))
- (%p-store-object-body (pheap object descend disk-cache address)
- (dc-%allocate-instance disk-cache (%p-store pheap class))
- (multiple-value-bind (slots slot-names)
- (dc-updated-instance-slots disk-cache address class pheap)
- (dotimes (i (length slot-names))
- (let ((slot-name (svref slot-names i)))
- (multiple-value-bind (value imm?)
- (if (slot-boundp object slot-name)
- (%p-store pheap (wood-slot-value object slot-name) descend)
- (values (%unbound-marker) t))
- (setf (dc-uvref disk-cache slots i imm?) value))))))))
-
- (defun p-load-instance (pheap disk-cache pointer depth subtype)
- (declare (ignore subtype))
- (let* ((cached? t)
- class
- (instance (maybe-cached-value pheap pointer
- (setq cached? nil)
- (if (null depth)
- (return-from p-load-instance (pptr pheap pointer)))
- (setq class (pointer-load pheap
- (dc-%svref disk-cache
- (dc-instance-class-wrapper
- disk-cache pointer)
- $wrapper.class)
- :default
- disk-cache))
- (allocate-instance class))))
- (when (or (not cached?)
- (and (eq depth t)
- (let ((p-load-hash (p-load-hash pheap)))
- (unless (gethash instance p-load-hash)
- (setf (gethash instance p-load-hash) instance)))))
- (let ((next-level-depth (cond ((or (eq depth :single) (fixnump depth)) nil)
- (t depth))))
- (multiple-value-bind (slot-vector slot-names real-slot-names)
- (dc-updated-instance-slots
- disk-cache pointer class pheap t)
- (dotimes (i (length slot-names))
- (let ((slot-name (svref slot-names i)))
- (when (or (null real-slot-names) (position slot-name real-slot-names))
- (multiple-value-bind (pointer immediate?)
- (dc-%svref disk-cache slot-vector i)
- (if immediate?
- (if (eq pointer (%unbound-marker))
- (slot-makunbound instance slot-name)
- (setf (wood-slot-value instance slot-name) pointer))
- (setf (wood-slot-value instance slot-name)
- (pointer-load pheap pointer next-level-depth disk-cache)))))))
- (when real-slot-names
- (dotimes (i (length real-slot-names))
- (let ((slot-name (svref real-slot-names i)))
- (unless (position slot-name slot-names)
- (slot-makunbound instance slot-name))))))))
- instance))
-
- ; These methods allow users to specialize the way that CLOS instances are saved.
-
- ; Return a vector of the names of the slots to be saved for an instance.
- ; The instance saving code assumes that multiple calls to this
- ; method will return the same (EQ) vector unless the class has been redefined.
- ; May be called with a CLASS-PROTOTYPE, so don't expect any of the slots
- ; to contain useful information.
- (defmethod wood-slot-names-vector ((object standard-object))
- (class-slots-vector (class-of object)))
-
- ; These allow specialization of slot-value.
- ; Some slots may want to be saved in a different format,
- ; or interned on the way back in.
- (defmethod wood-slot-value ((object standard-object) slot-name)
- (slot-value object slot-name))
-
- (defmethod (setf wood-slot-value) (value (object standard-object) slot-name)
- (setf (slot-value object slot-name) value))
-
-